/**
 * get mat(one dimension vector) value
 * @param mat one dimension vector
 * @param rows mat rows
 * @param cols mat cols
 * @param row row position
 * @param col col position
 * @return
 */
char visitOneDimVectorMat(vector<uchar> &mat, int rows, int cols, int row, int col) {
    assert(row < rows);
    assert(col < cols);
    return mat[row * cols + col];
}
#ifndef FH_PTHREAD_H
#define FH_PTHREAD_H

typedef struct {
    int pid;
    int start_row;
    int batch_size;
    int hh;
    int hw;
    Mat *src;
    Mat *dst;
    Mat *window;
} threadParam;

void *threadRow(void *p);

/**
 * 2 dimension gaussian filter
 * @param src
 * @param dst
 * @param window
 */
void pthreadGaussianFilter(Mat &src, Mat &dst, Mat window);

#endif //FH_PTHREAD_H

pthread_barrier_t barrier;

void *threadRow(void *p)
 {
    auto *param = (threadParam *) p;
    int start_row = param->start_row;
    int batch_size = param->batch_size;
    int hh = param->hh;
    int hw = param->hw;
    cout << param->dst << endl;

    for (int i = start_row; i < min(param->src->rows - hh, start_row + batch_size); i++) {
        for (int j = hw; j < param->src->cols - hw; j++) {
            double sum = 0;
            for (int r = -hh; r <= hh; ++r) {
                for (int c = -hw; c <= hw; ++c) {
                    sum = sum + param->src->at<double>(i + r, j + c) * param->window->at<double>(r + hh, c + hw);
                }
            }
            if (sum < 0)
                sum = 0;
            else if (sum > 255)
                sum = 255;
            param->dst->at<uchar>(i - hh, j - hw) = static_cast<uchar>(sum);
        }
    }
    pthread_barrier_wait(&barrier);
    pthread_exit(nullptr);
}

/**
 * 2 dimension gaussian filter
 * @param src
 * @param dst
 * @param window
 */
void pthreadGaussianFilter(Mat &src, Mat &dst, Mat window)
{
    int hh = (window.rows - 1) / 2;
    int hw = (window.cols - 1) / 2;
    dst = Mat::zeros(src.size(), src.type());
    cout << &dst << endl;

    //边界填充
    Mat newSrc;
    copyMakeBorder(src, newSrc, hh, hh, hw, hw, BORDER_REPLICATE);//边界复制
    /*初始化线程池*/
    const int THREAD_NUM = 4;
    pthread_t threadPool[THREAD_NUM];
    threadParam params[THREAD_NUM];
    int step = int((newSrc.rows) / THREAD_NUM);
    pthread_barrier_init(&barrier, nullptr, THREAD_NUM);
    for (int i = 0; i < THREAD_NUM; i++) {
        params[i].pid = i;
        params[i].src = &newSrc;
        params[i].dst = &dst;
        params[i].window = &window;
        params[i].start_row = i * step;
        params[i].batch_size = step;
        params[i].hh = hh;
        params[i].hw = hw;
        cout << params[i].dst << endl;
        pthread_create(&threadPool[i], nullptr, threadRow, (void *) &params[i]);
    }
    for (unsigned long long i : threadPool)
        pthread_join(i, nullptr);
    pthread_barrier_destroy(&barrier);
    newSrc.release();
}
